package com.jintian.smart.kernel.flowable.component.action.task;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;

import com.jintian.smart.kernel.core.flow.FlowAction;
import com.jintian.smart.kernel.core.flow.ProcessControl;
import com.jintian.smart.kernel.flowable.component.action.base.AbstractTaskAction;
import com.jintian.smart.kernel.flowable.context.DefaultProcessContext;
import com.jintian.smart.kernel.flowable.flowable.cmd.DeleteIdentityLinkForTaskCmd;
import com.jintian.smart.kernel.flowable.flowable.cmd.FetchTaskIdentityLinkEntityCmd;
import com.jintian.smart.kernel.flowable.service.CommentService;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.identitylink.api.IdentityLinkType;
import org.flowable.identitylink.service.impl.persistence.entity.IdentityLinkEntity;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;


@Component
public class FallbackTaskAction extends AbstractTaskAction {
    @Override
    public FlowAction getAction() {
        return FlowAction.fallback;
    }

    @Autowired
    private CommentService commentService;

    @Override
    protected List<TaskInfo> innerExecute(TaskEntity task, ProcessControl processControl) throws Exception {
        org.flowable.bpmn.model.Process process = this.getMainProcess(task.getProcessDefinitionId());
        FlowElement toActivity = null;
        if (processControl == null || processControl.getFlowTos().size() == 0) {
            // 默认退回到前驱环节
            List<UserTask> prevList = this.getPrevUserTask(task);

            Assert.isTrue(prevList.size() > 0, "没有前驱环节");
            toActivity = prevList.get(0);
        } else {
            // 指定了回退的环节
            if (processControl.getFlowTos().size() == 1) {
                ProcessControl.ProcessControlItem item = processControl.getFlowTos().get(0);
                toActivity = process.getFlowElement(item.getActivity());
            } else {
                // TODO 回退到多个环节？？？
                throw new IllegalArgumentException("暂不支持回退到多个环节");
            }
        }
        ProcessInstance pi = this.getProcessInstance(task.getProcessInstanceId());
        try (DefaultProcessContext context = (DefaultProcessContext) this.processContextProvider.createInstance(
                pi.getProcessDefinitionKey(), task.getTaskDefinitionKey(), this.getAction(),
                task.getProcessInstanceId(), task.getId(), pi.getBusinessKey(), processControl);) {
            this.beforeAction(task, context, this.getAction());
            FallbackTaskAction self = this;
            FlowElement targetActivity = toActivity;
            List<TaskInfo> ret = this.invokeAction(context, new Callable<List<TaskInfo>>() {
                @Override
                public List<TaskInfo> call() throws Exception {
                    return self.fallbackTask(task, process, targetActivity, context);
                }
            });
            return ret;
        }
    }

    private List<TaskInfo> fallbackTask(TaskEntity currentTask, org.flowable.bpmn.model.Process process, FlowElement targetActivity, DefaultProcessContext context) {
        this.moveActivityIdsToSingleActivityId(currentTask, process, targetActivity, context);
        List<TaskInfo> list = this.getFallbackOutTasks(currentTask);
        commentService.finish(context);
        return list;
    }

    private List<TaskInfo> getFallbackOutTasks(Task currentTask) {
        List<TaskInfo> list = this.activeTasks(currentTask.getProcessInstanceId());
        Assert.isTrue(list.size() == 1, "应产生唯一回退任务，实际任务数:" + list.size());
        TaskInfo nextTask = list.get(0);
        if (nextTask.getAssignee() == null && nextTask.getOwner() == null) {
            List<IdentityLinkEntity> links = this.managementService().executeCommand(new FetchTaskIdentityLinkEntityCmd(nextTask.getId()));
            links = links.stream().filter(e -> IdentityLinkType.CANDIDATE.equals(e.getType())).collect(Collectors.toList());
            if (links.size() > 0) {
            } else {
                taskService().setAssignee(nextTask.getId(), currentProcessContext().getOperatorId());
            }
        }
        return list;
    }

    private void moveActivityIdsToSingleActivityId(Task currentTask, org.flowable.bpmn.model.Process process,
                                                   FlowElement targetActivity, DefaultProcessContext context) {
        Set<String> moveActivityIds = new HashSet<>();
        List<? extends TaskInfo> beforeList = this.activeTasks(currentTask.getProcessInstanceId());
        for (TaskInfo e : beforeList) {
            // 必须要先删除IdentityLink，否则不能删除ru_task
            this.managementService().executeCommand(new DeleteIdentityLinkForTaskCmd(e.getId(), null, null));
            if (moveActivityIds.contains(e.getTaskDefinitionKey())) {
                continue;
            }
            UserTask sourceNode = (UserTask) process.getFlowElement(e.getTaskDefinitionKey());
            List<UserTask> prevUserTasks = new ArrayList<>();
            this.getPrevUserTask(sourceNode, prevUserTasks, null);
            prevUserTasks.remove(sourceNode);

            // 目标环节是执行实例的前驱
            prevUserTasks = prevUserTasks.stream().filter(a -> a.getId().equals(targetActivity.getId())).collect(Collectors.toList());
            boolean targetIsExecutionPrevActivity = prevUserTasks.size() == 1;
            if (targetIsExecutionPrevActivity) {
                moveActivityIds.add(e.getTaskDefinitionKey());
            }
        }

        Assert.isTrue(moveActivityIds.contains(currentTask.getTaskDefinitionKey()) && moveActivityIds.size() > 0, targetActivity.getName() + "不是当前环节的前驱环节");
        this.changeActivityStateBuilder().processInstanceId(currentTask.getProcessInstanceId())
                .moveActivityIdsToSingleActivityId(Arrays.asList(moveActivityIds.toArray(new String[0])), targetActivity.getId())// localVariables(startActivityId, localVariables)
                .changeState();
    }

    protected void moveExecutionsToSingleActivityId(Task currentTask, org.flowable.bpmn.model.Process process,
                                                    FlowElement targetActivity, DefaultProcessContext context) {
        List<Execution> allExecution = this.executionQuery().parentId(currentTask.getProcessInstanceId()).onlyChildExecutions().list();
        List<String> moveExecutionIds = new ArrayList<>();
        // 查询可回退并行执行实例(目标环节的后继环节)
        for (Execution e : allExecution) {
            UserTask sourceNode = (UserTask) process.getFlowElement(e.getActivityId());
            // if (sourceNode instanceof FlowNode) {
            List<UserTask> prevUserTasks = new ArrayList<>();
            this.getPrevUserTask(sourceNode, prevUserTasks, null);
            prevUserTasks.remove(sourceNode);
            // 目标环节是执行实例的前驱
            prevUserTasks = prevUserTasks.stream().filter(a -> a.getId().equals(targetActivity.getId())).collect(Collectors.toList());
            boolean targetIsExecutionPrevActivity = prevUserTasks.size() == 1;
            if (targetIsExecutionPrevActivity) {
                moveExecutionIds.add(e.getId());
            }
        }

        Assert.isTrue(moveExecutionIds.contains(currentTask.getExecutionId()) && moveExecutionIds.size() > 0, targetActivity.getName() + "不是当前环节的前驱环节");
        this.changeActivityStateBuilder().processInstanceId(currentTask.getProcessInstanceId()).moveExecutionsToSingleActivityId(moveExecutionIds, targetActivity.getId()).changeState();
    }

    @Override
    public List<? extends FlowNode> queryTaskNextActivityNode(Task task) {
        return this.getPrevUserTask(task);
    }
}
